<?php
/**
 * Note: 你好现在支付 无需进行限额/切换管理
 */
namespace App\Services\Pay;

use App\Abstracts\PayAbstract;
use App\Caches\Game\AppSourceCache;
use App\Caches\Game\PayChannelCache;
use App\Exceptions\ApiException;
use App\Exceptions\PayChannelEmptyException;
use App\Models\Game\Center\PayOrder;
use App\Models\Game\Center\WithdrawOrder;
use Illuminate\Support\Facades\Http;

class PayHelloNow extends PayAbstract
{
    public const CHANNEL = 'helloNow';

    private const API_QUERY_ORDER = 'https://pay.ipaynow.cn';

    private const API_VERSION = '1.0.3';
    private const API_H5_VERSION = '1.0.2';

    /**
     * 生成网关
     * @param $order
     * @return PayHelloNow
     * @throws PayChannelEmptyException
     */
    public function getPaymentByOrder($order): PayHelloNow
    {
        $appSource = AppSourceCache::get($order->source);
        if ($order instanceof PayOrder) {
            $this->payChannel = $this->getUseablePayChannel(self::CHANNEL, $order->price, $appSource->type, $order->gateway);
        } else if ($order instanceof WithdrawOrder) {
            $this->payChannel = $this->getUseableWithdrawChannel(self::CHANNEL, $order->money, $appSource->type);
        } else {
            throw new \Exception('invalid param: order');
        }
        $this->getConfig($this->payChannel);
        return $this;
    }

    /**
     * 根据 channelId 获取 payment
     * @param int $payChannelId
     * @return PayHelloNow
     */
    public function getPaymentByPayChannelId(int $payChannelId): PayHelloNow
    {
        $this->payChannel = PayChannelCache::get($payChannelId);
        $this->getConfig($this->payChannel);
        return $this;
    }

    /**
     * 获取支付数据
     * @param PayOrder $payOrder
     * @param string $notifyUrl
     * @return array
     */
    public function getPayData(PayOrder $payOrder, string $notifyUrl): string
    {
        $params['funcode'] = 'WP001';
        $params['version'] = self::API_VERSION;
        $params['appId'] = $this->payChannel->appid;
        $params['mhtOrderNo'] = $payOrder->orderid;
        // $params['mhtSubMchId'] = '';
        $params['mhtOrderName'] = $payOrder->package_name ?: '购买道具';
        $params['mhtOrderType'] = '05';
        $params['mhtCurrencyType'] = '156';// 156人民币
        $params['mhtOrderAmt'] = $payOrder->price;
        $params['mhtOrderDetail'] = $payOrder->package_name ?: '购买道具';
        // $params['mhtOrderTimeOut'] = '3600';
        // $params['mhtLimitPay'] = '0';
        $params['mhtOrderStartTime'] = $payOrder->created_at->format('YmdHis');
        $params['notifyUrl'] = $notifyUrl;
        $params['mhtCharset'] = 'UTF-8';
        $params['deviceType'] = '01';
        $params['payChannelType'] = '12';
        // 12支付宝 13微信 14银联
        if ($payOrder->gateway === 'alipay') {
            $params['payChannelType'] = '12';
        } else if ($payOrder->gateway === 'wechat') {
            $params['payChannelType'] = '13';
        }

        $params['consumerCreateIp'] = getClientIp();
        $params['mhtSignType'] = 'MD5';
        $params['mhtSignature'] = $this->makeSign($params, $this->payChannel->appkey);
        $paramStr = '';
        foreach ($params as $k => $v) {
            $paramStr .= "{$k}={$v}&";
        }
        return substr($paramStr, 0, -1);
    }

    /**
     * H5支付
     * @param PayOrder $payOrder
     * @param string $notifyUrl
     * @param string $returnUrl
     * @return string
     * @throws ApiException
     */
    public function getH5PayData(PayOrder $payOrder, string $notifyUrl, string $returnUrl): string
    {
        $params['funcode'] = 'WP001';
        $params['version'] = self::API_H5_VERSION;
        $params['appId'] = $this->payChannel->appid;
        $params['mhtOrderNo'] = $payOrder->orderid;
        // $params['mhtSubMchId'] = '';
        $params['mhtOrderName'] = $payOrder->package_name ?: '购买道具';
        $params['mhtOrderType'] = '01';
        $params['mhtCurrencyType'] = '156';// 156人民币
        $params['mhtOrderAmt'] = $payOrder->price;
        $params['mhtOrderDetail'] = $payOrder->package_name ?: '购买道具';
        // $params['mhtOrderTimeOut'] = '3600';
        // $params['mhtLimitPay'] = '0';
        $params['mhtOrderStartTime'] = $payOrder->created_at->format('YmdHis');
        $params['notifyUrl'] = $notifyUrl;
        $params['frontNotifyUrl'] = $returnUrl;
        $params['mhtCharset'] = 'UTF-8';
        $params['deviceType'] = '0601';
        $params['payChannelType'] = '12';
        // 12支付宝 13微信 20银联
        if ($payOrder->gateway === 'alipay') {
            $params['payChannelType'] = '12';
            $params['outputType'] = 2;// 返回alipays://xxxx或 https://xxxx
        } else if ($payOrder->gateway === 'wechat') {
            $params['payChannelType'] = '13';
            $params['outputType'] = 2;// 返回weixin://...
        }

        $params['consumerCreateIp'] = getClientIp();
        $params['mhtSignType'] = 'MD5';
        $params['mhtSignature'] = $this->makeSign($params, $this->payChannel->appkey);

        try {
            $res = Http::retry(1, 300)->timeout(5)->get(self::API_QUERY_ORDER, $params);
            $res->throw();
            info(__METHOD__, [$res]);
        } catch (\Exception $e) {
            report($e);
            return false;
        }
        $response = [];
        parse_str($res, $response);

        if ($response['responseCode'] !== 'A001') throw new ApiException($response['responseMsg'] ?? '支付订单创建失败, 请稍候重试');

        if (empty($response['tn'])) throw new ApiException('支付订单创建失败, 请稍候重试(1)');
        return $response['tn'];
    }

    /**
     * 订单校验
     * @param PayOrder $payOrder
     * @return bool
     */
    public function payOrderVerify(PayOrder $payOrder): bool
    {
        $params['funcode'] = 'MQ002';
        $params['version'] = self::API_VERSION;
        $params['deviceType'] = '01';
        $params['appId'] = $this->payChannel->appid;
        $params['mhtOrderNo'] = $payOrder->orderid;
        $params['mhtCharset'] = 'UTF-8';
        $params['mhtSignType'] = 'MD5';
        $params['mhtSignature'] = $this->makeSign($params, $this->payChannel->appkey);
        $queryStr = $this->makeSign($params, 'post');

        try {
            $res = Http::retry(1, 300)->timeout(5)->get(self::API_QUERY_ORDER.'?'.$queryStr);
            $res->throw();
            info(__METHOD__, [$res]);
        } catch (\Exception $e) {
            report($e);
            return false;
        }
        $response = [];
        parse_str($res, $response);
        return isset($response['tradeStatus']) && $response['tradeStatus'] === 'A001';
    }

    /**
     * H5支付校验
     * @param PayOrder $payOrder
     * @return bool
     */
    public function payH5OrderVerify(PayOrder $payOrder): bool
    {
        $params['funcode'] = 'MQ002';
        $params['version'] = self::API_H5_VERSION;
        $params['deviceType'] = '0601';
        $params['appId'] = $this->payChannel->appid;
        $params['mhtOrderNo'] = $payOrder->orderid;
        $params['mhtCharset'] = 'UTF-8';
        $params['mhtSignType'] = 'MD5';
        $params['mhtSignature'] = $this->makeSign($params, $this->payChannel->appkey);
        // $queryStr = $this->makeSign($params, 'post');

        try {
            $res = Http::retry(1, 300)->timeout(5)->get(self::API_QUERY_ORDER, $params);
            $res->throw();
            info(__METHOD__, [$res]);
        } catch (\Exception $e) {
            report($e);
            return false;
        }
        $response = [];
        parse_str($res, $response);
        return isset($response['transStatus']) && $response['transStatus'] === 'A001';
    }

    /**
     * 生成签名
     * @param array $params
     * @param string $key
     * @return string
     */
    public function makeSign(array $params, string $key): string
    {
        ksort($params);
        $signStr = '';
        foreach ($params as $k => $v) {
            if ($k === 'signature' || $v === '') continue;
            $signStr .= "{$k}={$v}&";
        }
        if ($key === 'post') {
            return substr($signStr, 0, -1);
        }
        return md5($signStr . md5($key));
    }

    /**
     * 签名校验
     * @param array $params
     * @return bool
     */
    public function verifySign(array $params): bool
    {
        $sign = $this->makeSign($params, $this->payChannel->appkey);
        return $sign === $params['signature'];
    }
}
